﻿#include "pch.h"
#if _QG_USE_D11
#include "qgdrv_d11_rdh.h"
#include "qgdrv_d11_cnv.h"

//////////////////////////////////////////////////////////////////////////
// D11

//
qgRasz* D11Rdh::CreateRasz(const qbRaszInitializer& inz)
{
	RDHNFO_INVOKE();

	const qbRaszData& data = inz.RaszData;

	D3D11_RASTERIZER_DESC rd;
	rd.FillMode = data.fill == QGFILL_SOLID ? D3D11_FILL_SOLID : D3D11_FILL_WIREFRAME;
	rd.CullMode = data.cull == QGCULL_BACK ? D3D11_CULL_BACK : data.cull == QGCULL_FRONT ? D3D11_CULL_FRONT : D3D11_CULL_NONE;
	rd.FrontCounterClockwise = false;	// 펄스!!!
	rd.DepthBias = (INT)(data.depth_bias * (float)(1 << 24));
	rd.DepthBiasClamp = 0.0f;
	rd.SlopeScaledDepthBias = data.slope_scale;
	rd.DepthClipEnable = true;
	rd.ScissorEnable = false;			// 사용하나!! 일단 펄스!!!
	rd.MultisampleEnable = false;
	rd.AntialiasedLineEnable = false;

	ID3D11RasterizerState* rs;
	if (FAILED(_device->CreateRasterizerState(&rd, &rs)))
	{
		kobj_tracep(KLV_ERROR, KERR_FAIL);
		return NULL;
	}

	D11Rasz* base = (D11Rasz*)_keep.rdos.Get(rs);
	if (base != NULL)
	{
		base->Load();
		rs->Release();
	}
	else
	{
		base = kobj_create(D11Rasz);
		base->_node_desc = K_CAST_PTR_TO_PUINT(rs);
		_keep.rdos.Add(base);
	}

	return base;
}

//
qgBlnd* D11Rdh::CreateBlnd(const qbBlndInitializer& inz)
{
	RDHNFO_INVOKE();

	D3D11_BLEND_DESC bd = {0, };
	bd.AlphaToCoverageEnable = false;
	bd.IndependentBlendEnable = inz.UseIndependent;

	for (ksize_t i = 0; i < QGSGOFF_MAX_VALUE; i++)
	{
		const qbBlndData& data = inz.BlndData[i];
		D3D11_RENDER_TARGET_BLEND_DESC& rd = bd.RenderTarget[i];
		rd.BlendEnable =
			data.color_op != QGBLO_ADD || data.color_src != QGBLF_ONE || data.color_dest != QGBLF_ZERO ||
			data.alpha_op != QGBLO_ADD || data.alpha_src != QGBLF_ONE || data.alpha_dest != QGBLF_ZERO;
		rd.SrcBlend = D11Convert::BlendFactor(data.color_src);
		rd.DestBlend = D11Convert::BlendFactor(data.color_dest);
		rd.BlendOp = D11Convert::BlendOp(data.color_op);
		rd.SrcBlendAlpha = D11Convert::BlendFactor(data.alpha_src);
		rd.DestBlendAlpha = D11Convert::BlendFactor(data.alpha_dest);
		rd.BlendOpAlpha = D11Convert::BlendOp(data.alpha_op);
		rd.RenderTargetWriteMask = (
			((data.mask & QGCM_RED) ? D3D11_COLOR_WRITE_ENABLE_RED : 0) |
			((data.mask & QGCM_GREEN) ? D3D11_COLOR_WRITE_ENABLE_GREEN : 0) |
			((data.mask & QGCM_BLUE) ? D3D11_COLOR_WRITE_ENABLE_BLUE : 0) |
			((data.mask & QGCM_ALPHA) ? D3D11_COLOR_WRITE_ENABLE_ALPHA : 0));
	}

#if 0
	k_mesgf("AlphaToCoverageEnable: %d\n", bd.AlphaToCoverageEnable);
	k_mesgf("IndependentBlendEnable: %d\n", bd.IndependentBlendEnable);
	for (kint i = 0; i < 8; i++)
	{
		k_mesgf("    %d - BlendEnable: %d\n", i, bd.RenderTarget[i].BlendEnable);
		k_mesgf("    %d - SrcBlend: %d\n", i, bd.RenderTarget[i].SrcBlend);
		k_mesgf("    %d - DestBlend: %d\n", i, bd.RenderTarget[i].DestBlend);
		k_mesgf("    %d - BlendOp: %d\n", i, bd.RenderTarget[i].BlendOp);
		k_mesgf("    %d - SrcBlendAlpha: %d\n", i, bd.RenderTarget[i].SrcBlendAlpha);
		k_mesgf("    %d - DestBlendAlpha: %d\n", i, bd.RenderTarget[i].DestBlendAlpha);
		k_mesgf("    %d - BlendOpAlpha: %d\n", i, bd.RenderTarget[i].BlendOpAlpha);
		k_mesgf("    %d - RenderTargetWriteMask: %d\n", i, bd.RenderTarget[i].RenderTargetWriteMask);
	}
#endif

	ID3D11BlendState* bs;
	HRESULT hr = _device->CreateBlendState(&bd, &bs);
	if (FAILED(hr))
	{
		kobj_tracep(KLV_ERROR, KERR_FAIL);
		kobj_tracef(KLV_ERROR, "HR = %08X", hr);
		return NULL;
	}

	D11Blnd* base = (D11Blnd*)_keep.rdos.Get(bs);
	if (base != NULL)
	{
		base->Load();
		bs->Release();
	}
	else
	{
		base = kobj_create(D11Blnd);
		base->_node_desc = K_CAST_PTR_TO_PUINT(bs);
		base->_independ = inz.UseIndependent;
		_keep.rdos.Add(base);
	}

	return base;
}

//
qgSplr* D11Rdh::CreateSplr(const qbSplrInitializer& inz)
{
	RDHNFO_INVOKE();

	const qbSplrData& data = inz.SplrData;

	D3D11_SAMPLER_DESC sd;
	D3D11_COMPARISON_FUNC cop = D11Convert::SamplerOp(data.cop);
	tpl::Color cc(data.bcv);
	sd.Filter = D11Convert::Filter(data.filt, cop != D3D11_COMPARISON_NEVER);
	sd.AddressU = D11Convert::Address(data.addru);
	sd.AddressV = D11Convert::Address(data.addrv);
	sd.AddressW = D11Convert::Address(data.addrw);
	sd.MipLODBias = (float)data.lod_bias;
	sd.MaxAnisotropy = 1;	//@ 이 값은 시스템 설정으로 처리할 것
	sd.ComparisonFunc = cop;
	sd.BorderColor[0] = cc.r;
	sd.BorderColor[1] = cc.g;
	sd.BorderColor[2] = cc.b;
	sd.BorderColor[3] = cc.a;
	sd.MinLOD = data.lod_range.min;
	sd.MaxLOD = data.lod_range.max;

	ID3D11SamplerState* ss;
	if (FAILED(_device->CreateSamplerState(&sd, &ss)))
	{
		kobj_tracep(KLV_ERROR, KERR_FAIL);
		return NULL;
	}

	D11Splr* base = (D11Splr*)_keep.rdos.Get(ss);
	if (base != NULL)
	{
		base->Load();
		ss->Release();
	}
	else
	{
		base = kobj_create(D11Splr);
		base->_node_desc = K_CAST_PTR_TO_PUINT(ss);
		_keep.rdos.Add(base);
	}

	return base;
}

//
qgDhsl* D11Rdh::CreateDhsl(const qbDhslInitializer& inz)
{
	RDHNFO_INVOKE();

	const qbDhslData& data = inz.DhslData;

	D3D11_DEPTH_STENCIL_DESC dsd;
	dsd.DepthEnable = data.depth_func != QGCOP_ALWAYS || data.depth_write;
	dsd.DepthWriteMask = data.depth_write ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
	dsd.DepthFunc = D11Convert::ComparisonOp(data.depth_func);
	dsd.StencilEnable = data.front_enable || data.back_enable;
	dsd.StencilReadMask = (UINT8)data.mask_read;
	dsd.StencilWriteMask = (UINT8)data.mask_write;
	dsd.FrontFace.StencilFunc = D11Convert::ComparisonOp(data.front_func);
	dsd.FrontFace.StencilFailOp = D11Convert::StencilOp(data.front_fail);
	dsd.FrontFace.StencilDepthFailOp = D11Convert::StencilOp(data.front_depth_fail);
	dsd.FrontFace.StencilPassOp = D11Convert::StencilOp(data.front_pass);
	if (!data.back_enable)
		dsd.BackFace = dsd.FrontFace;
	else
	{
		dsd.BackFace.StencilFunc = D11Convert::ComparisonOp(data.back_func);
		dsd.BackFace.StencilFailOp = D11Convert::StencilOp(data.back_fail);
		dsd.BackFace.StencilDepthFailOp = D11Convert::StencilOp(data.back_depth_fail);
		dsd.BackFace.StencilPassOp = D11Convert::StencilOp(data.back_pass);
	}

	const bool iskeep =
		data.front_pass == QGSOP_KEEP &&
		data.front_fail == QGSOP_KEEP &&
		data.front_depth_fail == QGSOP_KEEP &&
		data.back_pass == QGSOP_KEEP &&
		data.back_fail == QGSOP_KEEP &&
		data.back_depth_fail == QGSOP_KEEP;
	const bool iswrt = data.mask_write != 0 && !iskeep;

	ID3D11DepthStencilState* dss;
	if (FAILED(_device->CreateDepthStencilState(&dsd, &dss)))
	{
		kobj_tracep(KLV_ERROR, KERR_FAIL);
		return NULL;
	}

	D11Dhsl* base = (D11Dhsl*)_keep.rdos.Get(dss);
	if (base != NULL)
	{
		base->Load();
		dss->Release();
	}
	else
	{
		base = kobj_create(D11Dhsl);
		base->_node_desc = K_CAST_PTR_TO_PUINT(dss);
		base->_iskeep = iskeep;
		base->_iswrite = iswrt;
		_keep.rdos.Add(base);
	}

	return base;
}

//
struct D11LayoutKey : public D11Layout::Key
{
	explicit D11LayoutKey(int count, const qbLayoutElement* data)
	{
		for (kint n = 0; n < count; n++)
		{
			const qbLayoutElement& e = data[n];
			D3D11_INPUT_ELEMENT_DESC s = {0, };
			switch (e.usage)
			{
				case QGLOU_POSITION: s.SemanticName = "POSITION"; break;
				case QGLOU_COLOR: s.SemanticName = "COLOR"; break;
				case QGLOU_WEIGHT: s.SemanticName = "BLENDWEIGHT"; break;
				case QGLOU_INDEX: s.SemanticName = "BLENDINDICES"; break;
				case QGLOU_NORMAL: s.SemanticName = "NORMAL"; break;
				case QGLOU_TEXTURE: s.SemanticName = "TEXCOORD"; break;
				case QGLOU_TANGENT: s.SemanticName = "TANGENT"; break;
				case QGLOU_BINORMAL: s.SemanticName = "BINORMAL"; break;
				default: k_tracef(KLV_FATAL, "D11", "unknown vertex layout usage: %u", (kuint)e.usage);
			}
			s.SemanticIndex = e.index;
			switch (e.type)
			{
				case QGLOT_FLOAT1: s.Format = DXGI_FORMAT_R32_FLOAT; break;
				case QGLOT_FLOAT2: s.Format = DXGI_FORMAT_R32G32_FLOAT; break;
				case QGLOT_FLOAT3: s.Format = DXGI_FORMAT_R32G32B32_FLOAT; break;
				case QGLOT_FLOAT4: s.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; break;
				case QGLOT_HALF2: s.Format = DXGI_FORMAT_R16G16_FLOAT; break;
				case QGLOT_HALF4: s.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; break;
				case QGLOT_BYTE2: s.Format = DXGI_FORMAT_R8G8_UINT; break;
				case QGLOT_BYTE4: s.Format = DXGI_FORMAT_R8G8B8A8_UINT; break;
				case QGLOT_BYTE4N: s.Format = DXGI_FORMAT_R8G8B8A8_UNORM; break;
				case QGLOT_SHORT2: s.Format = DXGI_FORMAT_R16G16_SINT; break;
				case QGLOT_SHORT4: s.Format = DXGI_FORMAT_R16G16B16A16_SINT; break;
				case QGLOT_SHORT2N: s.Format = DXGI_FORMAT_R16G16_SNORM; break;
				case QGLOT_COLOR: s.Format = DXGI_FORMAT_R8G8B8A8_UNORM; break;
				default: k_tracef(KLV_FATAL, "D11", "unknown vertex layout element type: %u", (kuint)e.type);
			}
			s.InputSlot = e.slot;
			s.AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
			s.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
			s.InstanceDataStepRate = 0;
			list[n] = s;
		}

		// 0번
		list[0].AlignedByteOffset = 0;

		hash = k_crc32((const kbyte*)list.data(), count * sizeof(D3D11_INPUT_ELEMENT_DESC));
		used = count;
	}
};

//
qgVertexLayout* D11Rdh::CreateLayout(kint count, const qbLayoutElement* data)
{
	RDHNFO_INVOKE();

	k_return_value_if_fail(count > 0 && count < QGSGVERTEXLAYOUT_MAX_VALUE && data != NULL, NULL);

	D11LayoutKey key(count, data);

	D11Layout* base = (D11Layout*)_keep.vertexlayouts.Get(&key);
	if (base != NULL)
		base->Load();
	else
	{
		base = kobj_create(D11Layout);
		base->_key = key;

		for (kint i = 0; i < count; i++)
		{
			const qbLayoutElement& e = data[i];
			kint size = qgAchSupp::LayoutTypeToSize(e.type);
			base->_stride += size;
			base->_stage[e.slot] += (kushort)size;
		}

		_keep.vertexlayouts.Add(base);
	}

	return base;
}

//
qgBuffer* D11Rdh::CreateBuffer(qgBufferType type, kint stride, kint size, kint flags, kconstpointer data)
{
	RDHNFO_INVOKE();

	D11Buffer* base = kobj_create(D11Buffer);

	//
	D3D11_BUFFER_DESC& bd = base->_bd;
	D3D11_SUBRESOURCE_DATA sd, *psd;
	bd.ByteWidth = size;
	if (data != NULL)
	{
		bd.Usage = D3D11_USAGE_IMMUTABLE;
		bd.BindFlags = type == QGBUFFER_VERTEX ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER;
		bd.CPUAccessFlags = 0;
		psd = &sd;
		sd.pSysMem = data;
		sd.SysMemPitch = 0;
		sd.SysMemSlicePitch = 0;
	}
	else if (K_OFLAG(flags, QGRESF_DYNAMIC))
	{
		bd.Usage = D3D11_USAGE_DYNAMIC;
		bd.BindFlags = type == QGBUFFER_VERTEX ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER;
		bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		psd = NULL;
	}
	else if (K_OFLAG(flags, QGRESF_LOCAL))
	{
		// 스태이깅 = 예전의 SYSMEM. 
		// : 특정 용도로 바인딩 할 수가 없다.
		// : CPU 플래그가 반드시 지정되어야 한다
		bd.Usage = D3D11_USAGE_STAGING;
		bd.BindFlags = 0;
		bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;
		psd = NULL;
	}
	else
	{
		bd.Usage = D3D11_USAGE_DEFAULT;
		bd.BindFlags = type == QGBUFFER_VERTEX ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER;
		bd.CPUAccessFlags = 0;
		psd = NULL;
	}
	if (K_OFLAG(flags, QGRESF_STREAM) && !psd)
		bd.BindFlags |= D3D11_BIND_STREAM_OUTPUT;

	ID3D11Buffer* buf;
	if (FAILED(_device->CreateBuffer(&bd, psd, &buf)))
	{
		base->Unload();
		kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
		return NULL;
	}

	base->_type = (qgBufferType)type;
	base->_flags = flags;
	base->_stride = (kushort)stride;
	base->_size = size;
	base->_node_desc = K_CAST_PTR_TO_PUINT(buf);
	_keep.buffers.Append(base);

	return base;
}


//////////////////////////////////////////////////////////////////////////
// 공통 처리
namespace _d11
{
	//
	kconstpointer BaseKey(kNodeBase* ptr)
	{
		kObjNode* self = (kObjNode*)ptr;
		return K_CAST_UINT_TO_PTR(self->GetDescription());
	}

	//
	bool BaseEq(kNodeBase* ptr, kconstpointer k2)
	{
		kObjNode* self = (kObjNode*)ptr;
		return self->GetDescription() == K_CAST_PTR_TO_PUINT(k2);
	}

	//
	ksize_t KeyHash(kconstpointer k2)
	{
		return K_CAST_PTR_TO_PUINT(k2);
	}

	//
	void RdoUnload(qgRdo* base, bool releaseunknown = true)
	{
		if (releaseunknown)
		{
			IUnknown* unk = (IUnknown*)base->GetDescription();
			kunk_release(unk);
		}

		((D11Rdh*)qgRdh::Instance)->_RoamRdoUnload(base);
	}
}


//////////////////////////////////////////////////////////////////////////
// 래스터 라이저

//
void D11Rasz::TypeInitialize(kType type) throw()
{
	kTsb t(type);
	t.SetFuncKey(&_d11::BaseKey);
	t.SetFuncEq(&_d11::BaseEq);
	t.SetFuncHash(&_d11::KeyHash);
}

//
D11Rasz::~D11Rasz()
{
	_d11::RdoUnload(this);
}


//////////////////////////////////////////////////////////////////////////
// 블렌드

//
void D11Blnd::TypeInitialize(kType type) throw()
{
	kTsb t(type);
	t.SetFuncKey(&_d11::BaseKey);
	t.SetFuncEq(&_d11::BaseEq);
	t.SetFuncHash(&_d11::KeyHash);
}

//
D11Blnd::~D11Blnd()
{
	_d11::RdoUnload(this);
}


//////////////////////////////////////////////////////////////////////////
// 샘플러

//
void D11Splr::TypeInitialize(kType type) throw()
{
	kTsb t(type);
	t.SetFuncKey(&_d11::BaseKey);
	t.SetFuncEq(&_d11::BaseEq);
	t.SetFuncHash(&_d11::KeyHash);
}

//
D11Splr::~D11Splr()
{
	_d11::RdoUnload(this);
}


//////////////////////////////////////////////////////////////////////////
// 뎁스 스텐실

//
void D11Dhsl::TypeInitialize(kType type) throw()
{
	kTsb t(type);
	t.SetFuncKey(&_d11::BaseKey);
	t.SetFuncEq(&_d11::BaseEq);
	t.SetFuncHash(&_d11::KeyHash);
}

//
D11Dhsl::~D11Dhsl()
{
	_d11::RdoUnload(this);
}


//////////////////////////////////////////////////////////////////////////
// 레이아웃

//
void D11Layout::TypeInitialize(kType type) throw()
{
	kTsb t(type);
	t.SetFuncKey(&this_type::BaseKey);
	t.SetFuncEq((khEqFunc)&this_type::BaseEq);
	t.SetFuncHash((khHashFunc)&this_type::KeyHash);
}

//
D11Layout::~D11Layout() throw()
{
	if (_node_desc)
	{
		ID3D11InputLayout* il = (ID3D11InputLayout*)_node_desc;
		il->Release();
	}

	((D11Rdh*)qgRdh::Instance)->_RoamLayoutUnload(this);
}

//
bool D11Layout::_BakeShader(D11Shader* shd, ID3D11InputLayout** ret, D11Rdh* dev)
{
	// 실제 만든다...

	tpl::vector<kbyte>& keep = shd->_bd_vs->keep;
	if (keep.empty())
	{
		// 정점 세이더 코드 데이터가 없다!
		kobj_tracep(KLV_ERROR, KERR_NOTARGET);
		return false;
	}

	ID3D11InputLayout* il;
	HRESULT hr = dev->GetDevice()->CreateInputLayout(_key.list.data(), _key.used, keep.data(), keep.size(), &il);
	if (FAILED(hr))
	{
		// 헐... 못 만듦
		kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
		return false;
	}

	_node_desc = (kuintptr)il;
	if (!shd->_node_desc)
	{
		shd->_node_desc = (kuintptr)il;
		il->AddRef();
	}

	if (ret)
		*ret = il;

	return true;
}

//
kconstpointer D11Layout::BaseKey(kNodeBase* ptr)
{
	D11Layout* self = (D11Layout*)ptr;
	return &self->_key;
}

//
bool D11Layout::BaseEq(kNodeBase* ptr, const Key& k2)
{
	D11Layout* self = (D11Layout*)ptr;
	const Key& k1 = self->_key;

	if (k1.list.size() != k2.list.size())
		return false;

	for (ksize_t i = 0; i < k1.list.size(); i++)
	{
		if (memcmp(&k1.list[i], &k2.list[i], sizeof(D3D11_INPUT_ELEMENT_DESC)) != 0)
			return false;
	}

	return true;
}

//
ksize_t D11Layout::KeyHash(const Key& key)
{
	return key.hash;
}


//////////////////////////////////////////////////////////////////////////
// 버퍼

//
D11Buffer::~D11Buffer() throw()
{
	if (_node_desc)
	{
		if (_lock)
			D11Buffer::Unmap();

		ID3D11Buffer* buf = (ID3D11Buffer*)_node_desc;
		buf->Release();
	}

	((D11Rdh*)qgRdh::Instance)->_RoamBufferUnload(this);
}

//
kpointer D11Buffer::Map(kint flags)
{
	// 잠겨 있으면 실패
	if (_lock)
	{
		kobj_tracep(KLV_ERROR, KERR_MAPPING);
		return NULL;
	}

	// 초기 데이터가 있으면 잠글 수 없음
	if (_bd.Usage == D3D11_USAGE_IMMUTABLE)
	{
		kobj_tracep(KLV_ERROR, KERR_MAPPING);
		return NULL;
	}

	//
	D11Rdh* rdh = (D11Rdh*)qgRdh::Instance;
	ID3D11DeviceContext* context = rdh->GetContext();
	D3D11_MAPPED_SUBRESOURCE ms;

	//
	if (K_OFLAG(_flags, QGRESF_LOCAL) ||
		((K_OFLAG(_flags, QGRESF_DYNAMIC) && (flags == QGENTF_NONE || flags == QGENTF_WRITE))))
	{
		// 스테이깅이거나 다이나믹이면서 플래그가 없으면 바로 매핑
		ID3D11Buffer* buf = (ID3D11Buffer*)_node_desc;
		D3D11_MAP map;

		if (!K_OFLAG(flags, QGENTF_READ))
			map = D3D11_MAP_WRITE_DISCARD;
		else if (!K_OFLAG(flags, QGENTF_WRITE))
			map = D3D11_MAP_READ;
		else
			map = D3D11_MAP_READ_WRITE;

		if (FAILED(context->Map(buf, 0, map, 0, &ms)))
		{
			kobj_tracep(KLV_ERROR, KERR_MAPPING);
			return NULL;
		}
	}
	else
	{
		// 스태깅 임시 버퍼를 만들어서 반환...
		// 왜냐하면 D11은 스태깅 이외에는 거의 매핑할 수 없어 리소스 자체 복제를 해야함

		// 임시 버퍼 만들고
		D3D11_BUFFER_DESC bd;
		bd.ByteWidth = _bd.ByteWidth;
		bd.Usage = D3D11_USAGE_STAGING;
		bd.BindFlags = 0;
		bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;
		bd.MiscFlags = 0;
		bd.StructureByteStride = 0;

		if (FAILED(rdh->GetDevice()->CreateBuffer(&bd, NULL, &_stag)))
		{
			kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
			return NULL;
		}

		// 읽기가 가능하면... 읽게 해줘야지... ㅠ_ㅜ
		if (K_OFLAG(flags, QGENTF_READ))
		{
			ID3D11Buffer* buf = (ID3D11Buffer*)_node_desc;
			context->CopyResource(_stag, buf);
		}

		// 임시 스태깅으로 매핑
		if (FAILED(context->Map(_stag, 0, D3D11_MAP_READ_WRITE, 0, &ms)))
		{
			kobj_tracep(KLV_ERROR, KERR_MAPPING);
			return NULL;
		}

		// 까먹지 말고 세팅할 것
		_upload = flags != QGENTF_READ;	// 읽기 전용이 아니면 나중에 반드시 업로드해야함
	}

	//
	_lock = ms.pData;

	return ms.pData;
}

//
bool D11Buffer::Unmap()
{
	if (!_lock)
	{
		// 잠겨있지 않음
		kobj_tracep(KLV_ERROR, KERR_MAPPING);
		return false;
	}

	D11Rdh* rdh = (D11Rdh*)qgRdh::Instance;
	ID3D11DeviceContext* context = rdh->GetContext();

	ID3D11Buffer* buf = (ID3D11Buffer*)_node_desc;

	if (_stag)
	{
		// 임시 버퍼를 통한 처리
		context->Unmap(_stag, 0);

		if (_upload)
			context->CopyResource(buf, _stag);

		_stag->Release();
		_stag = NULL;;
	}
	else
	{
		// 바로 해제
		context->Unmap(buf, 0);
	}

	//
	_lock = NULL;

	return true;
}

#endif	// _QG_USE_D11
